package co.fitstart.mobile.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.Resource;

import org.apache.http.Consts;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.demo2do.core.BusinessException;
import com.demo2do.core.service.impl.GenericServiceImpl;
import com.demo2do.core.support.Result;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;
import com.demo2do.core.utils.JsonUtils;
import com.demo2do.core.utils.SignatureUtils;
import com.demo2do.core.utils.StringUtils;

import co.fitstart.entity.coupon.Coupon;
import co.fitstart.entity.coupon.CouponBatch;
import co.fitstart.entity.coupon.CouponStatus;
import co.fitstart.entity.coupon.CouponTemplate;
import co.fitstart.entity.coupon.DiscountType;
import co.fitstart.entity.coupon.Type;
import co.fitstart.entity.course.Course;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.order.OrderType;
import co.fitstart.entity.order.PaymentMethod;
import co.fitstart.entity.product.Product;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.User;
import co.fitstart.mobile.entity.trading.Shopping;
import co.fitstart.mobile.service.CouponService;

/**
 * 
 * @author mohanlan
 *
 */
@Service("couponService")
public class CouponServiceImpl extends GenericServiceImpl<Coupon> implements CouponService {
    
    @Value("#{ctx['api.host']}")
    private String APIHOST;
    
    @Resource(name = "payment")
    private Properties properties;
    
    private static final Logger logger = LoggerFactory.getLogger(CouponServiceImpl.class);

    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCouponTemplates(java.util.List)
     */
    @SuppressWarnings("unchecked")
    public List<CouponTemplate> listCouponTemplates(List<Long> templateIds) {
        
        String sentence = "FROM CouponTemplate couponTemplate WHERE couponTemplate.disabled = false AND couponTemplate.id in (:ids)";
        
        return genericDaoSupport.searchForList(sentence, "ids", templateIds);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCouponBatch(java.util.List)
     */
    @SuppressWarnings("unchecked")
    public List<CouponBatch> listCouponBatch(List<Long> batchIds) {
        
        String sentence = "FROM CouponBatch couponBatch WHERE couponBatch.id in (:ids)";
        return genericDaoSupport.searchForList(sentence, "ids", batchIds);
    }

    /**
     * 
     * @param couponTemplate
     * @return
     */
    @SuppressWarnings("unchecked")
    protected CouponBatch loadCouponBacth(CouponTemplate couponTemplate) {
        
        String sentence = "FROM CouponBatch couponBatch WHERE couponBatch.couponTemplate = :couponTemplate AND couponBatch.amount > couponBatch.releaseAmount ORDER BY couponBatch.createTime ASC";
        
        List<CouponBatch> couponBatchs = genericDaoSupport.searchForList(sentence, "couponTemplate", couponTemplate);
        return couponBatchs.size() > 0 ? couponBatchs.get(0) : null;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#createCoupons(co.fitstart.entity.user.User, java.util.List)
     */
    @SuppressWarnings("unchecked")
    public void createCoupons(User user, List<CouponBatch> couponBatchs) {
        
        for(CouponBatch couponBatch: couponBatchs) {
            
            Map<String, Object> parameters = new HashMap<String, Object>();
            parameters.put("userId", user.getId());
            parameters.put("couponBatch", couponBatch);
            
            List<Coupon> userCoupons = genericDaoSupport.searchForList("FROM Coupon coupon WHERE coupon.userId = :userId AND coupon.batch = :couponBatch", parameters);
            
            if(couponBatch != null && userCoupons.size() == 0) {
                Coupon coupon = new Coupon(couponBatch);
                genericDaoSupport.save(coupon.bindUser(user.getId()));
                genericDaoSupport.update(couponBatch.editClaimedAmount());
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#createCoupons(java.lang.String, java.util.List)
     */
    public void createCoupons(String mobile, List<CouponBatch> couponBatchs) {
        User user = genericDaoSupport.searchForObject("FROM User WHERE disabled = false AND mobile = :mobile", "mobile", mobile, User.class);
        
        if(user != null) {
            this.createCoupons(user, couponBatchs);
        } else {
            this.unRegisterCouponClaim(mobile, couponBatchs);
        }
        
    }
    
    /**
     * 
     * @param mobile
     * @param couponBatchs
     */
    @SuppressWarnings("unchecked")
    private void unRegisterCouponClaim(String mobile, List<CouponBatch> couponBatchs) {
        
        for(CouponBatch couponBatch: couponBatchs) {
            
            Map<String, Object> parameters = new HashMap<String, Object>();
            parameters.put("mobile", mobile);
            parameters.put("couponBatch", couponBatch);
            
            List<Coupon> userCoupons = genericDaoSupport.searchForList("FROM Coupon coupon WHERE identification = :mobile AND coupon.batch = :couponBatch", parameters);
            
            if(couponBatch != null && userCoupons.size() == 0) {
                Coupon coupon = new Coupon(couponBatch);
                genericDaoSupport.save(coupon.identify(mobile));
                genericDaoSupport.update(couponBatch.editClaimedAmount());
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weixin.service.CouponService#listCoupon(co.fitstart.common.entity.user.User)
     */
    @SuppressWarnings("unchecked")
    public List<Coupon> listCoupon(User user) {
        String sentence = "FROM Coupon WHERE userId = :userId AND credit = false ORDER BY status ASC";
        return genericDaoSupport.searchForList(sentence, "userId", user.getId());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCreditCoupons(co.fitstart.entity.user.User)
     */
    @SuppressWarnings("unchecked")
    public List<Coupon> listCreditCoupons(User user) {
        String sentence = "FROM Coupon WHERE userId = :userId AND credit = true ORDER BY status ASC";
        return genericDaoSupport.searchForList(sentence, "userId", user.getId());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCoupon(co.fitstart.entity.user.User, java.util.List)
     */
    @SuppressWarnings("unchecked")
    public List<Coupon> listCoupon(User user, List<Long> batchIds) {
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userId", user.getId());
        parameters.put("batchIds", batchIds);
        
        String sentence = "FROM Coupon WHERE userId = :userId AND batch.id in (:batchIds)";
        
        return genericDaoSupport.searchForList(sentence, parameters);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCoupon(co.fitstart.entity.user.User, co.fitstart.entity.product.Product)
     */
    @SuppressWarnings("unchecked")
    public List<Coupon> listCoupon(User user, Product product) {
        Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userId", user.getId());
        parameters.put("status", CouponStatus.CLAIMED);
        parameters.put("today", today);
        parameters.put("percent", DiscountType.PERCENT);
        parameters.put("fixedAmount", DiscountType.FIXED_AMOUNT);
        parameters.put("price", product.getCurrentOffer());
        parameters.put("all", Type.ALL);
        parameters.put("category", Type.CATEGORY);
        parameters.put("productType", product.getType());
        parameters.put("product", Type.PRODUCT);
        parameters.put("toBuy", product);
        
        return genericDaoSupport.searchForList("FROM Coupon coupon"
                + " WHERE coupon.userId = :userId AND coupon.status = :status"
                + " AND :today BETWEEN coupon.effectiveStartDate AND coupon.effectiveEndDate"
                + " AND (coupon.template.discountType = :percent OR (coupon.template.discountType = :fixedAmount AND :price >= coupon.template.minAmount))"
                + " AND (coupon.type = :all OR (coupon.type = :category AND coupon.template.productType = :productType) OR (coupon.type = :product AND coupon.template.product = :toBuy))", parameters);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#syncCoupon(co.fitstart.entity.user.User, co.fitstart.entity.product.Product)
     */
    @SuppressWarnings("unchecked")
    public int syncCoupon(User user, Product product) {
        
        // sync coupon status
        String mobile = user.getMobile();
        
        List<Coupon> syncCoupons = genericDaoSupport.searchForList("FROM Coupon coupon WHERE coupon.identification = :mobile", "mobile", mobile);
        for(Coupon coupon : syncCoupons) {
            genericDaoSupport.update(coupon.bindUser(user.getId()));
        }
        
        List<Coupon> coupons = this.listCoupon(user, product);
        
        return coupons.size();
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#loadCouponBatch(java.lang.Long)
     */
    public CouponBatch loadCouponBatch(Long couponBathId) {
        return genericDaoSupport.load(CouponBatch.class, couponBathId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#loadCoupon(java.lang.String, co.fitstart.entity.coupon.CouponBatch)
     */
    public Coupon loadCoupon(String mobile, CouponBatch couponBatch) {
        String sentence = "FROM Coupon WHERE batch = :batch AND identification = :mobile";
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("batch", couponBatch);
        parameters.put("mobile", mobile);
        
        return genericDaoSupport.searchForObject(sentence, parameters, Coupon.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#loadCoupon(java.lang.Long, co.fitstart.entity.coupon.CouponBatch)
     */
    public Coupon loadCoupon(Long userId, CouponBatch couponBatch) {
        String sentence = "FROM Coupon WHERE batch = :batch AND userId = :userId";
        
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("batch", couponBatch);
        parameters.put("userId", userId);
        
        return genericDaoSupport.searchForObject(sentence, parameters, Coupon.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#createCoupon(co.fitstart.entity.coupon.CouponBatch, java.lang.String, java.lang.String, java.lang.String)
     */
    public Coupon createCoupon(CouponBatch couponBatch, String mobile, String contactPerson, String wechatNumber) {
        
        Coupon coupon = new Coupon(couponBatch, couponBatch.getExpireType(), mobile, contactPerson, wechatNumber);
        
        User user = genericDaoSupport.searchForObject("FROM User user WHERE user.mobile = :mobile AND user.disabled = false", "mobile", mobile, User.class);
        if(user != null) {
            coupon.bindUser(user.getId());
        } 
        genericDaoSupport.save(coupon);
        genericDaoSupport.update(couponBatch.editClaimedAmount());
        return coupon;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#createOrder(co.fitstart.entity.coupon.Coupon, co.fitstart.entity.user.User, java.lang.Long)
     */
    public Order createOrder(Coupon coupon, User user, Long siteId) {
        Shopping shopping = new Shopping(user, coupon, siteId);
        
     // 1. generate order no
        String orderNo = shopping.createOrderNo();  // FIXME fix order no generate logic
        
        // 2. load product info
        
        Product product = genericDaoSupport.load(Product.class, shopping.getProductId());
        
        // 3. load user info & update user profile name if not exit
        user.setName(shopping.getContact().getContactPerson());
        user.editWechatCode(shopping.getWechatNumber());
        genericDaoSupport.update(user);
        
        // 4. create order data and add related order attr
        Order order = shopping.createOrder();
        
        order.initialize(orderNo, user, product)
             .addAttribute("createTime", DateFormatUtils.format(order.getCreateTime(), "yyyy-MM-dd HH:mm:SS"));
        
        // 5. init contact with person and phone
        order.addAttribute("contactPerson", shopping.getContact().getContactPerson())
             .addAttribute("contactPhone", shopping.getContact().getContactPhone());
        
        // 6. initialize site for order
        if(shopping.withSite()) { 
            
            Site site = genericDaoSupport.load(Site.class, shopping.getSiteId());
            order.site(site);
            
            order.addAttribute("siteId", shopping.getSiteId());
        }
        
        // 7. bind coupon & calculate price
            order.calculateExperienceCouponPrice();
            
            order.addAttribute("couponId", shopping.getCouponId());
            
            genericDaoSupport.update(coupon.occupy());
            
        
        // 9. init order type
        Course course = product.getCourse();
        if(course == null) {
            order.initType(OrderType.OTHERS);
            
        }else if(course != null && course.isOnline()) {
            order.initType(OrderType.ONLINE);
            
        } else if(course != null && course.isYear()) {
            order.initType(OrderType.YEAR);
            
        } else if(course != null && course.isOffline() && course.isGroup()) {
            order.initType(OrderType.GROUP);
            
        } else {
            order.initType(OrderType.INDIVIDUAL);
        }
        
        // 10. save into database
        genericDaoSupport.save(order);
        
        return order;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#payOrder(co.fitstart.entity.order.Order, co.fitstart.entity.coupon.Coupon)
     */
    public Result payOrder(Order order, Coupon coupon) {
        
        String orderNo = order.getCode();
        Long customerId = order.getUser().getId();
        int amount = order.getAmount();
        String requestNo = "ADMIN_PAY";
        String channel = PaymentMethod.COUPON.getAlias();
        
        Result result = this.payOrder(orderNo, customerId, amount, requestNo, channel);
        if(!result.isValid()) {
            genericDaoSupport.update(order.disable());
            genericDaoSupport.executeHQL("DELETE FROM OrderAttribute WHERE order = :order", "order", order);
            genericDaoSupport.update(coupon.claimed());
            
            logger.error("activateCoupon() error : fail to pay order");
        }
        return result;
    }
    
    
    /**
     * 
     * @param orderNo
     * @param customerId
     * @param amount
     * @param requestNo
     * @param channel
     * @return
     */
    private Result payOrder(String orderNo, Long customerId, int amount, String requestNo, String channel) {
        
        Result result = new Result();
        
        try {
            
            String appId = properties.getProperty("appid");
            String appsecret = properties.getProperty("appsecret");
            
            Map<String, String> parameters = new LinkedHashMap<String, String>();
            parameters.put("appid", appId);
            parameters.put("channel", channel);
            parameters.put("order_no", orderNo);
            parameters.put("customer_id", customerId.toString());
            parameters.put("amount", String.valueOf(amount));
            parameters.put("pay_time", DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"));
            parameters.put("request_no", requestNo);
            
            String queryString = StringUtils.toSortedQueryString(parameters) + "&key=" + appsecret;
            String signature = SignatureUtils.makeMD5Signature(queryString).toUpperCase();
            
            parameters.put("signature", signature);
            String content = JsonUtils.toJsonString(parameters);
            
            Response response = Request.Post(APIHOST + "/v1/payments/callback")
                                       .bodyString(content, ContentType.APPLICATION_JSON)
                                       .connectTimeout(100000)
                                       .socketTimeout(100000)
                                       .execute();
            
            String body = response.returnContent().asString(Consts.UTF_8);
            logger.trace("PaymentHandlerImpl#prePay() - Get response body [{}].", body);
            
            result = JSON.parseObject(body, Result.class);
            
        } catch (Exception e) {
            logger.error("PaymentHandlerImpl#prePay() - Requesting wechat pay order api euncouters IO exception: [{}]", e);
        }
        
        return result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#listCoupon(co.fitstart.entity.order.Order)
     */
    @SuppressWarnings("unchecked")
    public List<Coupon> listCoupon(Order order) {
        return genericDaoSupport.searchForList("FROM Coupon WHERE order = :order", "order", order);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.CouponService#give(co.fitstart.entity.coupon.Coupon, co.fitstart.entity.user.User, co.fitstart.entity.user.User)
     */
    public void give(Coupon coupon, User from, User to) {
        if(coupon == null || !from.getId().equals(coupon.getUserId()) 
                || !coupon.getStatus().isClaimed() || coupon.getRestDay() <= 0 
                || coupon.getTemplate().getDiscountType().isFixedAmount() 
                || coupon.getOriginalUserId() != null) {
            throw new BusinessException("优惠券异常");
        } else if(to ==  null) {
            throw new BusinessException("不存在的受赠用户");
        } else if(to == from) {
            throw new BusinessException("不能转赠给自己");
        }
        
        Order order = coupon.getOrder();
        
        //check coupons from one online year order can only give one user
        List<Coupon> coupons = listCoupon(order);
        for(Coupon eachCoupon : coupons) {
            if(!from.getId().equals(eachCoupon.getUserId()) && !to.getId().equals(eachCoupon.getUserId())) {
                throw new BusinessException("只能转赠给一个人");
            }
        }
        
        genericDaoSupport.update(coupon.give(from, to));
    }
}
